package dbx

import (
	"fmt"
	"net/netip"
	"net/url"
	"strings"
)

/*
sslmode			Eavesdropping protection	MITM protection	Statement
disable			No	No	I don't care about security, and I don't want to pay the overhead of encryption.
allow			Maybe	No	I don't care about security, but I will pay the overhead of encryption if the server insists on it.
prefer			Maybe	No	I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it.
require			Yes	No	I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want.
verify-ca		Yes	Depends on CA policy	I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust.
verify-full		Yes	Yes	I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify.
*/

type PostgresSQL struct {
	DataBase DataBase `json:"database" yaml:"database" toml:"database"`
	Ssl      TLS      `json:"ssl,omitempty" yaml:"ssl,omitempty" toml:"ssl,omitempty"`
}

// dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
func (r PostgresSQL) dsnOptions(values url.Values, query bool) string {
	if values == nil {
		values = url.Values{}
	}

	if !values.Has("TimeZone") {
		values.Add("TimeZone", "Asia/Shanghai")
	}

	if !r.Ssl.IsEnable() {
		values.Set("sslmode", "disable")
	} else {
		values.Set("sslmode", r.Ssl.Mode)
		values.Set("sslrootcert", r.Ssl.CaCert)
		values.Set("sslcert", r.Ssl.Cert)
		values.Set("sslkey", r.Ssl.Key)
	}

	encode, err := url.QueryUnescape(values.Encode())
	if err != nil {
		panic(err)
	}

	if !query {
		encode = strings.ReplaceAll(encode, "&", " ")
	}

	return encode
}

func (r PostgresSQL) DSN(query url.Values) string {
	parseAddr, err := netip.ParseAddrPort(r.DataBase.Address)
	if err != nil {
		panic(err)
	}

	builder := strings.Builder{}
	builder.WriteString(
		fmt.Sprintf("user=%v password=%v host=%v port=%v dbname=%v ",
			r.DataBase.UserName, r.DataBase.Password, parseAddr.Addr().String(), parseAddr.Port(), r.DataBase.DbName),
	)

	options := r.dsnOptions(query, false)
	builder.WriteString(options)

	return builder.String()
}
func (r PostgresSQL) DSNX(query url.Values) string {
	u := url.URL{
		Scheme: "postgres",
		User:   url.UserPassword(r.DataBase.UserName, r.DataBase.Password),
		Host:   r.DataBase.Address,
		Path:   r.DataBase.DbName,
	}

	u.RawQuery = r.dsnOptions(query, true)

	return u.String()
}
